package com.skivingcloud.report.datas.service.impl;

import com.skivingcloud.admin.sys.entity.User;
import com.skivingcloud.common.contants.Constant;
import com.skivingcloud.common.exception.BusinessException;
import com.skivingcloud.common.utils.JsonUtils;
import com.skivingcloud.common.utils.PageUtils;
import com.skivingcloud.common.utils.UuidUtil;
import com.skivingcloud.report.bigscreen.entity.ChartDto;
import com.skivingcloud.report.contants.JdbcConstants;
import com.skivingcloud.report.datas.entity.OriginalDataDto;
import com.skivingcloud.report.datas.entity.ReportDataSource;
import com.skivingcloud.report.datas.entity.ReportDataset;
import com.skivingcloud.report.datas.entity.ReportDatasetParam;
import com.skivingcloud.report.datas.entity.ReportDatasetTransform;
import com.skivingcloud.report.datas.entity.SetTypeEnum;
import com.skivingcloud.report.datas.mapper.ReportDataSourceMapper;
import com.skivingcloud.report.datas.mapper.ReportDatasetMapper;
import com.skivingcloud.report.datas.mapper.ReportDatasetParamMapper;
import com.skivingcloud.report.datas.mapper.ReportDatasetTransformMapper;
import com.skivingcloud.report.datas.service.IReportDataSourceService;
import com.skivingcloud.report.datas.service.IReportDatasetParamService;
import com.skivingcloud.report.datas.service.IReportDatasetService;
import com.skivingcloud.report.datas.service.IReportDatasetTransformService;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 数据集管理 服务实现类
 * </p>
 *
 * @author skivingcloud
 * @since 2022-07-20
 */
@Service
public class ReportDatasetServiceImpl extends ServiceImpl<ReportDatasetMapper, ReportDataset> implements IReportDatasetService {

	private static Logger log = LoggerFactory.getLogger(ReportDatasetServiceImpl.class);
	
	private ScriptEngine engine;
    {
        ScriptEngineManager manager = new ScriptEngineManager();
        engine = manager.getEngineByName("JavaScript");
    }

	@Autowired
	private ReportDatasetParamMapper reportDatasetParamMapper;
	@Autowired
	private ReportDatasetTransformMapper reportDatasetTransformMapper;
	@Autowired
	private ReportDataSourceMapper reportDataSourceMapper;

	@Autowired
	private IReportDataSourceService reportDataSourceService;
    @Autowired
	private IReportDatasetTransformService reportDatasetTransformService;
    @Autowired
	private IReportDatasetParamService reportDatasetParamService;
    
	/**
	 * 获取数据集
	 * @param dto
	 * @return
	 * @throws JsonProcessingException
	 * @throws JsonMappingException
	 */
	@Override
	public OriginalDataDto getDatasetOriginal(ChartDto dto) throws Exception {
		OriginalDataDto originalDataDto = new OriginalDataDto();
		String setCode = dto.getSetCode();
		//1.获取数据集、参数替换、数据转换
        ReportDataset dataSetDto = detailSet(setCode);
        String dynSentence = dataSetDto.getDynSentence();
		//2.获取数据源
        ReportDataSource dataSource;
        if (StringUtils.isNotBlank(dataSetDto.getSetType())
                && dataSetDto.getSetType().equals(SetTypeEnum.HTTP.getCodeValue())) {
            //http不需要数据源，兼容已有的逻辑，将http所需要的数据塞进DataSource
            dataSource = new ReportDataSource();
            dataSource.setSourceConfig(new ObjectMapper().readTree(dynSentence));
            dataSource.setSourceType(JdbcConstants.HTTP);
            String body = JSONObject.fromObject(dynSentence).getString("body");
            if (StringUtils.isNotBlank(body)) {
                dynSentence = body;
            }else {
                dynSentence = "{}";
            }

        }else {
            dataSource  = reportDataSourceMapper.selectOne(
				new QueryWrapper<ReportDataSource>().lambda()
				.eq(ReportDataSource::getSourceCode, dataSetDto.getSourceCode()));
        }
		//3.参数替换
        //3.1参数校验
        log.debug("参数校验替换前：{}", dataSetDto.getContextData());
        boolean verification = verification(dataSetDto.getDataSetParamList(), dataSetDto.getContextData());
        if (!verification) {
            throw new BusinessException("规则参数校验不通过");
        }
        dynSentence = reportDatasetParamService.transform(dataSetDto.getContextData(), dynSentence);
        log.debug("参数校验替换后：{}", dataSetDto.getContextData());
        //4.获取数据
        dataSource.setDynSentence(dynSentence);
        dataSource.setContextData(dataSetDto.getContextData());
        //获取total,判断contextData中是否传入分页参数
        if (null != dataSetDto.getContextData()
                && dataSetDto.getContextData().containsKey("pageNumber")
                && dataSetDto.getContextData().containsKey("pageSize")) {
            long total = reportDataSourceService.total(dataSource, dataSetDto);
            originalDataDto.setTotal(total);
        }
        List<JSONObject> data = reportDataSourceService.execute(dataSource);
        List<JsonNode> rd = data.stream().map(obj -> {
           try {
            return new ObjectMapper().readTree(JsonUtils.beanToJson(obj));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList());
        //5.数据转换
        List<JsonNode> transform = reportDatasetTransformService.transform(dataSetDto.getDataSetTransformList(), rd);
        originalDataDto.setData(transform);
        return originalDataDto;
	}


	/**
     * 单条详情
     *
     * @param setCode
     * @return
     */
    @Override
	public ReportDataset detailSet(String setCode) {
		QueryWrapper<ReportDataset> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("set_code", setCode);
		ReportDataset set = this.baseMapper.selectOne(queryWrapper);
		return getDetailSet(set, setCode);
	}

	private ReportDataset getDetailSet(ReportDataset set, String setCode) {
		List<ReportDatasetParam> dataSetParamList = reportDatasetParamMapper.selectList(
                new QueryWrapper<ReportDatasetParam>()
                        .lambda()
                        .eq(ReportDatasetParam::getSetCode, setCode)
        );
		set.setDataSetParamList(dataSetParamList);
		
        List<ReportDatasetTransform> dataSetTransformList = reportDatasetTransformMapper.selectList(
                new QueryWrapper<ReportDatasetTransform>()
                    .lambda()
                    .eq(ReportDatasetTransform::getSetCode, setCode)
                    .orderByAsc(ReportDatasetTransform::getOrderNum)
        );
		set.setDataSetTransformList(dataSetTransformList);
		if (StringUtils.isNotBlank(set.getCaseResult())) {
            try {
                JSONArray jsonArray = JSONArray.fromObject(set.getCaseResult());
                JSONObject jsonObject = jsonArray.getJSONObject(0);
                set.setSetParamList(jsonObject.keySet());
            } catch (Exception e) {
                log.error("error",e);
            }
        }
        return set;
	}

	/**
	 * 参数校验  js脚本
	 * @param dataSetParamList
	 * @param contextData
	 * @return
	 * @throws Exception
	 */
	private boolean verification(List<ReportDatasetParam> dataSetParamList, Map<String, Object> contextData) throws Exception {
		if (null == dataSetParamList || dataSetParamList.size() == 0) {
            return true;
        }

        for (ReportDatasetParam dataSetParamDto : dataSetParamList) {
            if (null != contextData) {
                String value = contextData.getOrDefault(dataSetParamDto.getParamName(), "").toString();
                dataSetParamDto.setSampleItem(value);
            }

            final Object verification = verification(dataSetParamDto);
            if (verification instanceof Boolean) {
                if (!(Boolean) verification) {
                    return false;
                }
            }else {
                //将得到的值重新赋值给contextData
                if (null != contextData) {
                    contextData.put(dataSetParamDto.getParamName(), verification);
                }
                dataSetParamDto.setSampleItem(verification.toString());
            }

        }
        return true;
	}

	/**
	 * 参数校验  js脚本
	 * @param dataSetParamDto
	 * @return
	 * @throws Exception
	 */
	private Object verification(ReportDatasetParam dataSetParamDto) throws Exception {
		String validationRules = dataSetParamDto.getValidationRules();
        if (StringUtils.isNotBlank(validationRules)) {
            try {
                engine.eval(validationRules);
                if(engine instanceof Invocable){
                    Invocable invocable = (Invocable) engine;
                    Object exec = invocable.invokeFunction("verification", dataSetParamDto);
                    ObjectMapper objectMapper = new ObjectMapper();
                    if (exec instanceof Boolean) {
                        return objectMapper.convertValue(exec, Boolean.class);
                    }else {
                        return objectMapper.convertValue(exec, String.class);
                    }

                }

            } catch (Exception ex) {
                throw ex;
            }

        }
        return true;
	}


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        PageUtils pageUtils = PageUtils.getPageInfo(params);
		Page<ReportDataset> p = PageHelper.startPage(pageUtils.getPage(), pageUtils.getLimit());
		ReportDataset reportDataset = (ReportDataset) JSONObject.toBean(
				JSONObject.fromObject(params.get("dataForm")),
				ReportDataset.class);
		QueryWrapper<ReportDataset> queryWrapper = new QueryWrapper<>();
		if (StringUtils.isNotBlank(reportDataset.getSourceCode())) {
			queryWrapper.eq("source_code", reportDataset.getSourceCode());
		}
        if (StringUtils.isNotBlank(reportDataset.getSetType())) {
			queryWrapper.eq("set_type", reportDataset.getSetType());
		}
		if (StringUtils.isNotBlank(reportDataset.getSetCode())) {
			queryWrapper.like("set_code", "%" + reportDataset.getSetCode() + "%");
		}
        if (StringUtils.isNotBlank(reportDataset.getSetName())) {
			queryWrapper.like("set_name", "%" + reportDataset.getSetName() + "%");
		}
		this.baseMapper.selectList(queryWrapper);
		pageUtils = new PageUtils(p);
		return pageUtils;
    }


    @Override
    public OriginalDataDto testTransform(ReportDataset dto) throws Exception {
        String dynSentence = dto.getDynSentence();

        OriginalDataDto originalDataDto = new OriginalDataDto();
        String sourceCode = dto.getSourceCode();
        //1.获取数据源
        ReportDataSource dataSource;
        if (dto.getSetType().equals(SetTypeEnum.HTTP.getCodeValue())) {
            //http不需要数据源，兼容已有的逻辑，将http所需要的数据塞进DataSource
            dataSource = new ReportDataSource();
            dataSource.setSourceConfig(new ObjectMapper().readTree(dynSentence));
            dataSource.setSourceType(JdbcConstants.HTTP);
            String body = JSONObject.fromObject(dynSentence).getString("body");
            if (StringUtils.isNotBlank(body)) {
                dynSentence = body;
            }else {
                dynSentence = "{}";
            }

        }else {
            
          dataSource  = reportDataSourceMapper.selectOne(
            new QueryWrapper<ReportDataSource>().lambda()
            .eq(ReportDataSource::getSourceCode, sourceCode));
        }

        //3.参数替换
        //3.1参数校验
        boolean verification = verification(dto.getDataSetParamList(), null);
        if (!verification) {
            throw new BusinessException("规则参数校验不通过");
        }

        dynSentence = reportDatasetParamService.transform(dto.getDataSetParamList(), dynSentence);
        //4.获取数据
        dataSource.setDynSentence(dynSentence);
        dataSource.setContextData(dto.getContextData());

        //获取total,判断DataSetParamDtoList中是否传入分页参数
        Map<String, Object> collect = dto.getDataSetParamList().stream().collect(Collectors.toMap(ReportDatasetParam::getParamName, ReportDatasetParam::getSampleItem));
        if (collect.containsKey("pageNumber") && collect.containsKey("pageSize")) {
            dto.setContextData(collect);
            long total = reportDataSourceService.total(dataSource, dto);
            originalDataDto.setTotal(total);
        }

        List<JSONObject> data = reportDataSourceService.execute(dataSource);
        List<JsonNode> rd = data.stream().map(obj -> {
            try {
             return new ObjectMapper().readTree(JsonUtils.mapToJson(obj));
             } catch (JsonProcessingException e) {
                 throw new RuntimeException(e);
             }
         }).collect(Collectors.toList());
        //5.数据转换
        List<JsonNode> transform = reportDatasetTransformService.transform(dto.getDataSetTransformList(), rd);
        originalDataDto.setData(transform);
        return originalDataDto;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(ReportDataset dataSet, User user) {
        LocalDateTime nowTime = LocalDateTime.now();
        List<ReportDatasetParam> dataSetParamList = dataSet.getDataSetParamList();
        List<ReportDatasetTransform> dataSetTransformList = dataSet.getDataSetTransformList();
        dataSet.setId(UuidUtil.get32UUID());
        dataSet.setEnableFlag("1");
        dataSet.setDeleteFlag("0");
        dataSet.setCreater(user.getId());
        dataSet.setVersion(0);
        dataSet.setCreateTime(nowTime);
        this.baseMapper.insert(dataSet);
        reportDatasetParamMapper.delete(new QueryWrapper<ReportDatasetParam>().lambda().eq(ReportDatasetParam::getSetCode, dataSet.getSetCode()));
        if(dataSetParamList != null && !dataSetParamList.isEmpty()){
            dataSetParamList.forEach(dsp -> {
                dsp.setSetCode(dataSet.getSetCode());
                dsp.setId(UuidUtil.get32UUID());
                dsp.setEnableFlag("1");
                dsp.setDeleteFlag("0");
                dsp.setCreater(user.getId());
                dsp.setCreateTime(nowTime);
                reportDatasetParamMapper.insert(dsp);
            });
        }
        reportDatasetTransformMapper.delete(new QueryWrapper<ReportDatasetTransform>().lambda().eq(ReportDatasetTransform::getSetCode, dataSet.getSetCode()));
        if(dataSetTransformList != null && !dataSetTransformList.isEmpty()){
            dataSetTransformList.forEach(dst -> {
                dst.setSetCode(dataSet.getSetCode());
                dst.setId(UuidUtil.get32UUID());
                dst.setEnableFlag("1");
                dst.setDeleteFlag("0");
                dst.setCreater(user.getId());
                dst.setCreateTime(nowTime);
                reportDatasetTransformMapper.insert(dst);
            });
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(ReportDataset dataSet, User user) {
        ReportDataset old = this.baseMapper.selectById(dataSet.getId());
        LocalDateTime nowTime = LocalDateTime.now();
        List<ReportDatasetParam> dataSetParamList = dataSet.getDataSetParamList();
        List<ReportDatasetTransform> dataSetTransformList = dataSet.getDataSetTransformList();
        old.setSetDesc(dataSet.getSetDesc());
        old.setSetName(dataSet.getSetName());
        old.setSourceCode(dataSet.getSourceCode());
        old.setDynSentence(dataSet.getDynSentence());
        // old.setCaseResult(dataSet.getCaseResult());
        old.setModUser(user.getId());
        old.setModTime(nowTime);
        old.setVersion(old.getVersion() + 1);
        old.setSetType(dataSet.getSetType());
        this.baseMapper.updateById(old);
        reportDatasetParamMapper.delete(new QueryWrapper<ReportDatasetParam>().lambda().eq(ReportDatasetParam::getSetCode, dataSet.getSetCode()));
        if(!dataSetParamList.isEmpty()){
            dataSetParamList.forEach(dsp -> {
                dsp.setSetCode(dataSet.getSetCode());
                dsp.setId(UuidUtil.get32UUID());
                dsp.setEnableFlag("1");
                dsp.setDeleteFlag("0");
                dsp.setCreater(user.getId());
                dsp.setCreateTime(nowTime);
                reportDatasetParamMapper.insert(dsp);
            });
        }
        reportDatasetTransformMapper.delete(new QueryWrapper<ReportDatasetTransform>().lambda().eq(ReportDatasetTransform::getSetCode, dataSet.getSetCode()));
        if(!dataSetTransformList.isEmpty()){
            dataSetTransformList.forEach(dst -> {
                dst.setSetCode(dataSet.getSetCode());
                dst.setId(UuidUtil.get32UUID());
                dst.setEnableFlag("1");
                dst.setDeleteFlag("0");
                dst.setCreater(user.getId());
                dst.setCreateTime(nowTime);
                reportDatasetTransformMapper.insert(dst);
            });
        }
    }


    @Override
    public ReportDataset selectById(String id) {
        ReportDataset dataSet = this.baseMapper.selectById(id);
        List<ReportDatasetParam> dataSetParamList = reportDatasetParamMapper.selectList(new QueryWrapper<ReportDatasetParam>().lambda().eq(ReportDatasetParam :: getSetCode, dataSet.getSetCode()));
        dataSet.setDataSetParamList(dataSetParamList);
		
        List<ReportDatasetTransform> dataSetTransformList = reportDatasetTransformMapper.selectList(
                new QueryWrapper<ReportDatasetTransform>()
                    .lambda()
                    .eq(ReportDatasetTransform::getSetCode, dataSet.getSetCode())
                    .orderByAsc(ReportDatasetTransform::getOrderNum)
        );
		dataSet.setDataSetTransformList(dataSetTransformList);
        return dataSet;
    }


    @Override
    public OriginalDataDto testTransformBySetId(String id) throws Exception {
        ReportDataset dto = selectById(id);
        return testTransform(dto);
    }


    @Override
    public List<ReportDataset> queryAllDataSet() {
        LambdaQueryWrapper<ReportDataset> wrapper = Wrappers.lambdaQuery();
        wrapper.select(ReportDataset::getSetCode, ReportDataset::getSetName, ReportDataset::getSetDesc, ReportDataset::getId)
                .eq(ReportDataset::getEnableFlag, Constant.COMMON_YES_OR_NO_YES);
        wrapper.orderByDesc(ReportDataset::getModTime);
        return this.baseMapper.selectList(wrapper);
    }


    @Override
    public ReportDataset detailBysetId(String id) {
        ReportDataset reportDataset = selectById(id);
        if (StringUtils.isNotBlank(reportDataset.getCaseResult())) {
            try {
                JSONArray jsonArray = JSONArray.fromObject(reportDataset.getCaseResult());
                JSONObject jsonObject = jsonArray.getJSONObject(0);
                reportDataset.setSetParamList(jsonObject.keySet());
            } catch (Exception e) {
                log.error("error",e);
            }
        }
        return reportDataset;
    }

}
